﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Imaging;
using System.Drawing;
using System.ComponentModel;
using System.Threading.Tasks;

namespace Common.Drawing
{
    public static class BitmapImageHelper
    {
        private const int RGBMax = 3;
        private const int MagicPixelPaddingCode = 255;

        /// <summary>
        /// Gets the RGB pixels of a bitmap image as bytes.
        /// </summary>
        /// <param name="strFilePath">File name to a bitmap.</param>
        /// <returns>RGB as byte array. Ex: One pixel equals three bytes.</returns>
        public static byte[] GetARGB(string strFilePath)
        {
            if (String.IsNullOrEmpty(strFilePath)) return null;

            Bitmap bit = new Bitmap(strFilePath);
            Color curclr;
            byte[] RawData = new byte[(bit.Height * bit.Width) * RGBMax];

            #region "Un-Used Code for a Loop" 
            // region is the wrong color it should be blue
            //for (int y = 0; y < bit.Height; y++)
            //{
            //    for (int x = 0; x < bit.Width; x++)
            //    {
            //        curclr = bit.GetPixel(x, y);
            //        RawData[(x + (y * bit.Width)) * RGBMax + 0] = curclr.R;
            //        RawData[(x + (y * bit.Width)) * RGBMax + 1] = curclr.G;
            //        RawData[(x + (y * bit.Width)) * RGBMax + 2] = curclr.B;
            //    }
            //}
            #endregion
            
            Parallel.For(0, bit.Height, y =>
                {
                    // TODO: Check to see if any loops definded in the 
                    //Parallel.For is Thread orelse we will have to create Sub Parallel loops
                    Parallel.For(0, bit.Width, x =>
                        {
                            curclr = bit.GetPixel(x, y);
                            RawData[(x + (y * bit.Width)) * RGBMax + 0] = curclr.R;
                            RawData[(x + (y * bit.Width)) * RGBMax + 1] = curclr.G;
                            RawData[(x + (y * bit.Width)) * RGBMax + 2] = curclr.B;
                        }
                    );
                }
            );



            // Removes Magic Pixel Padding from the end of array.
            return RawData.Reverse()
                          .SkipWhile(
                                      p =>
                                          p == MagicPixelPaddingCode.ToByte()
                                    ).Reverse()
                                     .ToArray<byte>();
        }

        /// <summary>
        /// Converts a byte array to a bitmap. Ex: Three bytes will equal one pixel.
        /// Adds padding to the end, if byte array is not a even factor of three.
        /// </summary>
        /// <param name="RawData">Data to be converted to a RGB pixel.</param>
        /// <returns>Bitmap of the data.</returns>
        public static Bitmap SetARGB(byte[] RawData)
        {
            if (RawData.isNull() || RawData.Length < 1) return null;
            int iTotalPixels = RawData.Length / RGBMax;
            int w = System.Math.Ceiling(System.Math.Sqrt(iTotalPixels)).ToInt();
            int h = System.Math.Ceiling(System.Math.Sqrt(iTotalPixels)).ToInt();
            Bitmap bit = new Bitmap(w, h);
            int[] iRGB = new int[3];

            #region "Un-Code for a loop"
            //for (int y = 0; y < bit.Height; y++)
            //{
            //    for (int x = 0; x < bit.Width; x++)
            //    {
            //        iRGB.SetValue(MagicPixelPaddingCode, 0, 1, 2);

            //        try
            //        {
            //            iRGB[0] = RawData[(x + (y * bit.Width)) * RGBMax + 0];
            //            iRGB[1] = RawData[(x + (y * bit.Width)) * RGBMax + 1];
            //            iRGB[2] = RawData[(x + (y * bit.Width)) * RGBMax + 2];
            //        }
            //        catch (IndexOutOfRangeException)
            //        {
            //        }

            //        bit.SetPixel(x, y, Color.FromArgb(iRGB[0], iRGB[1], iRGB[2]));
            //    }
            //}
            #endregion

            // even calling the GC still leaves memory leaks, same if you where to Dispose of objects

            Parallel.For(0, bit.Height, y =>
                {
                    // TODO: Check to see if any loops definded in the 
                    //Parallel.For is Thread orelse we will have to create Sub Parallel loops
                    Parallel.For(0, bit.Width, x =>
                        {
                            iRGB.SetValue(MagicPixelPaddingCode, 0, 1, 2);

                            try
                            {
                                iRGB[0] = RawData[(x + (y * bit.Width)) * RGBMax + 0];
                                iRGB[1] = RawData[(x + (y * bit.Width)) * RGBMax + 1];
                                iRGB[2] = RawData[(x + (y * bit.Width)) * RGBMax + 2];
                            }
                            catch (IndexOutOfRangeException)
                            {
                            }

                            bit.SetPixel(x, y, Color.FromArgb(iRGB[0], iRGB[1], iRGB[2]));
                        }
                    );
                }
            );

            ParallelQuery<byte> plquery = RawData.AsParallel();

            plquery.For(0, bit.Height, y =>
                {
                    for (int x = 0; x < bit.Width; x++)
                    {
                        iRGB.SetValue(MagicPixelPaddingCode, 0, 1, 2);

                        try
                        {
                            iRGB[0] = RawData[(x + (y * bit.Width)) * RGBMax + 0];
                            iRGB[1] = RawData[(x + (y * bit.Width)) * RGBMax + 1];
                            iRGB[2] = RawData[(x + (y * bit.Width)) * RGBMax + 2];
                        }
                        catch (IndexOutOfRangeException)
                        {
                        }

                        bit.SetPixel(x, y, Color.FromArgb(iRGB[0], iRGB[1], iRGB[2]));
                    }
                }
            );


            return bit;
        }
    }
}
